package org.springframework.web.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Autowried;
import org.springframework.context.annotation.Controller;
import org.springframework.context.annotation.RequestMapping;
import org.springframework.context.annotation.RequestParam;
import org.springframework.context.annotation.Service;

public class DispatcherServlet extends HttpServlet{
	
	private Properties properties;
	
	private List<String> classNames = new ArrayList<String>();
	/**
	 * ioc容器
	 */
	private Map<String, Object> ioc = new HashMap<String, Object>();
	
	/**
	 * handlerMapping
	 */
	//private Map<Handler> handlerMapping = new HashMap<String, Method>();
	private List<Handler> handlerMapping = new ArrayList<DispatcherServlet.Handler>();
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public void init(ServletConfig config) throws ServletException {
		//获取配置文件所在的路径
		String location = config.getInitParameter("contextConfigLocation");
		System.out.println("配置文件路径："+location);
		//实现Spring一样的IOC容器初始化、DI注入、MVC的url对应Method(解析Controller)
		
		//1、加载配置文件中的数据
		doLoadConfig(location);
		//2、扫描配置文件中的相关所有的类
		doScan(properties.getProperty("scanPackage"));
		//3、实例化被所有扫描的类，并且存放到IOC容器中，自己实现IOC容器
		doInstance();
		
		//4、依赖注入，DI从ioc容器中找到有@Autowried注解的属性，并且找到其在IOC容器中的实例，动态赋值
		doAutowired();
		//5、把加了@Controller的类中加了@RequestMapping这个注解的方法和URL构造成一个对应关系
		initHandlerMapping();
		//6、等待用户请求，根据用户请求的URL在Map中找到其对应的Method，并通过反射机制动态调用方法执行
		
		System.out.println("MVC framework");
	}
	
	private void initHandlerMapping() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Entry<String, Object> entry : ioc.entrySet()) {
			Class<?> clazz = entry.getValue().getClass();
			String url = "";
			if (clazz.isAnnotationPresent(Controller.class)) {
				RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
				url = requestMapping.value();
			}
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (method.isAnnotationPresent(RequestMapping.class)) {
					RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
					String mapping = requestMapping.value();
					StringBuffer path = new StringBuffer();
					String regex = path.append("/").append(url).append("/").append(mapping).toString().replaceAll("/+", "/");
					Pattern pattern = Pattern.compile(regex);
					handlerMapping.add(new Handler(entry.getValue(), method, pattern));
				}
			}
		}
	}

	/**
	 * 依赖注入
	 */
	private void doAutowired() {
		if (ioc.isEmpty()) {
			return;
		}
		for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			System.out.println(entry.getValue());
			Object bean = entry.getValue();
			Field[] fields = bean.getClass().getDeclaredFields();

			for (Field field : fields) {
				if (field.isAnnotationPresent(Autowried.class)) {
					Autowried autowried = field.getAnnotation(Autowried.class);
					String beanName = autowried.value().trim();
					if ("".equals(beanName)) {
						beanName = field.getType().getName();
					}
					//即使是private的属性也要执行
					field.setAccessible(true);
					try {
						field.set(entry.getValue(), ioc.get(beanName));
					} catch (IllegalArgumentException | IllegalAccessException e) {
						e.printStackTrace();
						continue;
					}
				}
			}
		
			
		}
		
	}

	/**
	 * 实例化对象
	 */
	private void doInstance() {
		if (classNames.isEmpty()) {
			return;
		}
		try {
			for (String className : classNames) {
				System.out.println(className);
				Class<?> clazz = Class.forName(className);
				if (clazz.isAnnotationPresent(Service.class)) {
					//第一种形式，默认首字母小写
					//第二种形式，如果自己取了名字，优先自己定义的名字
					//第三中形式，利用接口本身全程作为key，把其对应实现类的实例作为值
					Service service = clazz.getAnnotation(Service.class);
					String beanName = service.value();
					Object bean = clazz.newInstance();
					if (!"".equals(beanName.trim())) {
						ioc.put(beanName, bean);
						continue;
					}
					beanName = lowerFirst(clazz.getSimpleName());
					ioc.put(beanName, bean);
					
					Class<?>[] interfaces = clazz.getInterfaces();
					for (Class<?> i : interfaces) {
						ioc.put(i.getName(), bean);
					}
				}else if (clazz.isAnnotationPresent(Controller.class)) {
					String beanName = lowerFirst(clazz.getSimpleName());
					ioc.put(beanName, clazz.newInstance());
				}else {
					continue;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * 扫描包，得到class
	 * @param packageName
	 */
	private void doScan(String packageName) {
		String path = "/"+packageName.replace(".", "/");
		System.out.println(path);
		URL url = this.getClass().getClassLoader().getResource(path);
		File dir = new File(url.getFile());
		for (File file : dir.listFiles()) {
			if (file.isDirectory()) {
				doScan(packageName+"."+file.getName());
			}else {
				classNames.add(packageName + "."+file.getName().replace(".class", "").trim());
			}
		}
	}

	/**
	 * 加载配置文件
	 * @param location
	 */
	private void doLoadConfig(String location) {
		InputStream is = null;
		try {
			is = this.getClass().getClassLoader().getResourceAsStream(location);
			properties = new Properties();
			properties.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			doDispatcher(request, response);
		} catch (Exception e) {
			response.getWriter().write("500 exception ,details:\r\n"+Arrays.toString(e.getStackTrace()));
		}
		
	}

	private void doDispatcher(HttpServletRequest request, HttpServletResponse response) {
		if (handlerMapping.isEmpty()) {
			return;
		}
		/*String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");*/
		//Method method = handlerMapping.get(url);
		//method.invoke(obj, args);
		try {
			Handler handler = getHandler(request);
			if (handler == null) {
				response.getWriter().write("404 not found");
				return;
			}
			//获取参数列表
			Class<?>[] paramTypes = handler.method.getParameterTypes();
			//保存所有需要自动赋值的参数值
			Object[] paramValues = new Object[paramTypes.length];
			
			Map<String, String[]> params = request.getParameterMap();
			for (Entry<String, String[]> param : params.entrySet()) {
				String value = param.getValue()[0];
				if (handler.paramIndexMapping.containsKey(param.getKey())) {
					int index = handler.paramIndexMapping.get(param.getKey());
					paramValues[index] = convert(paramTypes[index], value);
				}
			}
			
			
			Integer reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
			Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
			if (reqIndex != null) {
				paramValues[reqIndex] = request;
			}
			if (respIndex != null) {
				paramValues[respIndex] = response;
			}
			
			handler.method.invoke(handler.controller, paramValues);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private Object convert(Class<?> type, String value) {
		if (Integer.class == type) {
			return Integer.valueOf(value);
		}
		return value;
	}

	private String lowerFirst(String str){
		char[] chars = str.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
	private Handler getHandler(HttpServletRequest request) throws Exception{
		if (handlerMapping.isEmpty()) {
			return null;
		}
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		for (Handler handler : handlerMapping) {
			Matcher matcher = handler.pattern.matcher(url);
			if (matcher.matches()) {
				return handler;
			}
		}
		return null;
	}
	
	private class Handler{
		/**保存方法对应的实例*/
		protected Object controller;
		/**保存映射的方法*/
		private Method method;
		protected Pattern pattern;
		/**参数顺序*/
		protected Map<String, Integer> paramIndexMapping;
		
		
		protected Handler(Object controller, Method method, Pattern pattern) {
			super();
			this.controller = controller;
			this.method = method;
			this.pattern = pattern;
			this.paramIndexMapping = new HashMap<String, Integer>();
			putParamIndexMapping(method);
		}

		private void putParamIndexMapping(Method method){
			Annotation[][] pa = method.getParameterAnnotations();
			for (int i = 0; i < pa.length; i++) {
				for (Annotation a : pa[i]) {
					if (a instanceof RequestParam) {
						String paramName = ((RequestParam)a).value();
						if (!"".equals(paramName.trim())) {
							paramIndexMapping.put(paramName, i);
						}
					}
				}
			}
			
			Class<?>[] paramsTypes = method.getParameterTypes();
	
			for (int i = 0; i < paramsTypes.length; i++) {
				Class<?> type = paramsTypes[i];
				if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
					paramIndexMapping.put(type.getName(),i);
				}
			}
		}

		
	}
}
